package com.smileus.integration.provider;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.smileus.common.Env;
import com.smileus.entity.ServiceInfo;
import com.smileus.entity.ServiceRouter;
import com.smileus.service.ClusterService;
import com.smileus.service.ServiceInfoService;
import com.smileus.util.JSONUtil;

/**
 * doService,doBatch无法在抽象类里做，否则 ws无法暴露服务，所以写此工具类
 * 
 */
@SuppressWarnings("unchecked")
public class AbstractServiceProvider {
	private static final Logger logger = Logger.getLogger(AbstractServiceProvider.class);
	protected volatile boolean isRunning = false; // -1:服务停止 ，1：服务正常
	protected static ClusterService clusterService = Env.getBean("defaultClusterService", ClusterService.class);
	private static ServiceInfoService serviceInfoService = Env.getBean("serviceInfoService", ServiceInfoService.class);
	private static ServiceRouter cfgRouter = clusterService.getCurtServiceRouter();

	/**
	 * tasks格式：[{"sId:userService.add,ps:[param1,param2]",seq:123}
	 * 
	 * @param ServiceProvider
	 * @return String
	 */
	public String doBatch(String tasks) {
		JSONArray resultArray = new JSONArray();
		JSONObject result = null;
		String serviceId = null;
		String param = null;
		JSONObject json = null;
		JSONArray list = JSONArray.parseArray(tasks);
		for (Object object : list) {
			result = new JSONObject();
			json = JSONUtil.toEntity(object, JSONObject.class);
			serviceId = json.getString("sId");
			param = json.getString("ps");
			long seq = json.getLongValue("seq");
			result.put("seq", seq);
			result.put("result", doService(serviceId, param));
			resultArray.add(result);
		}
		return resultArray.toJSONString();
	}

	/**
	 * 要保证入参为JSON字符串，如果没有参数，用"" 当前服务器对外提供的服务统一入口 ；
	 * 
	 * serviceId形式如：userService.add，userService为当前机器上spring bean
	 * id，add为当前bean的方法；
	 * 
	 * param：调用接口入参，建议每一个具体服务的入参均为JSONObject，这样可简化具体服务调用
	 */

	public String doService(String serviceId, String param) {
		logger.debug("call start," + "serviceId:" + serviceId + "; param:" + param);
		JSONObject resultWrap = new JSONObject();
		Object result = null;
		if (param == null || "".equals(param)) {
			param = "[]";
		}
		try {
			/**
			 *serviceId形式如：userService.add，userService为当前机器上spring bean id,
			 * add为当前bean的方法, param转为方法对应参数，通过反射执行，得到结果；
			 */
			result = invoke(serviceId, param);// 反射调用，取得执行结果
			resultWrap.put("result", JSONObject.toJSONString(result));
			resultWrap.put("success", true);// 1代表成功，0代表失败
			resultWrap.put("desc", "success");
		} catch (Exception e) {
			e.printStackTrace();
			resultWrap.put("success", false);// 1代表成功，0代表失败
			resultWrap.put("desc", "there has some error");
			// 错误日志记录
			logger.error("CRM call error," + "serviceId:" + serviceId + "; param:" + param);
			logger.error(e);
			// 告知监控系统出现错误
		}
		return resultWrap.toString();
	}

	/**
	 * 在servlet停止是调用
	 */
	public void stop() {
		// 1.停止当前服务器的服务
		ServiceRouter serviceRouter = clusterService.getCurtServiceRouter();
		serviceRouter.setServiceStatus(-1);
		// 2.告诉监视器当前服务已停止
		clusterService.saveOrUpdateServiceRouter(serviceRouter);
		// 刷新集群环境
		clusterService.clusterRefresh();
	}

	/**
	 * 获取是否为运行状态
	 * 
	 * @return
	 */
	public boolean isRunning() {
		return isRunning;
	}

	public void clusterRefresh() {
		clusterService.refreshRunningMap();
	}

	public static Object invoke(String serviceId, String param) throws Exception {
		String[] array = null;
		if (serviceId == null || (array = serviceId.split("\\.")).length != 2) {
			throw new RuntimeException("不合法的serviceId，请检查格式是否为:xxService.save形式");
		}
		// 取得method对象
		ServiceInfo serviceInfo = serviceInfoService.getServiceInfo(cfgRouter.getSysDomain(), serviceId);
		if (serviceInfo == null) {
			throw new RuntimeException("请先配置当前服务" + serviceId + "的信息。");
		}
		// 等待温绍改进 JSONArray.parseArray 方法 -.-
		List<String> typeList = JSONArray.parseArray(serviceInfo.getParamArray(), String.class);
		List<String> valueList = JSONArray.parseArray(param, String.class);
		Method method = getServiceMethod(serviceId);
		Class[] paramTypes = method.getParameterTypes();
		String beanId = array[0];
		Object serviceBean = Env.getBean(beanId);
		int paramCount = paramTypes.length;
		Object[] params = new Object[paramCount];
		// 参数以JSONArray的形式存放
		if (typeList.size() != valueList.size()) {
			throw new RuntimeException("放入的参数个数与实际接口服务信息配置的参数个数不一样");
		}
		if (paramCount != valueList.size()) {
			throw new RuntimeException("放入的参数个数与实际接口参数不一样");
		}
		for (int i = 0; i < paramCount; i++) {
			params[i] = JSONUtil.toObject(valueList.get(i), typeList.get(i));
		}
		return method.invoke(serviceBean, params);
	}

	static final Map<Class, Method[]> methodsMap = new HashMap<Class, Method[]>();
	static final Map<String, Method> serviceMethodMap = new HashMap<String, Method>();

	public static Method getServiceMethod(String serviceId) {
		if (serviceMethodMap.containsKey(serviceId)) {
			return serviceMethodMap.get(serviceId);
		}
		String[] array = null;
		if (serviceId == null || (array = serviceId.split("\\.")).length != 2) {
			throw new RuntimeException("不合法的serviceId，请检查格式是否为:xxService.save形式");
		}
		String beanId = array[0];
		String methodName = array[1];
		Object bean = Env.getBean(beanId);
		Class clazz = bean.getClass();
		Method[] methods = null;
		if (methodsMap.containsKey(clazz)) {
			methods = methodsMap.get(clazz);
		} else {
			methods = clazz.getMethods();
			methodsMap.put(clazz, methods);
		}

		Set<Method> mSet = new HashSet<Method>();
		Method result = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				mSet.add(method);
				result = method;
			}
		}
		if (mSet.size() == 0) {
			throw new RuntimeException("找不到bean：" + beanId + "中的方法：：" + methodName);
		}
		// 服务层方法中，最好不要出现重载的服务分发
		if (mSet.size() > 1) {
			throw new RuntimeException("bean：" + beanId + "中存在多个方法：" + methodName);
		}
		serviceMethodMap.put(serviceId, result);
		return result;
	}
}
